home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Snippets / Processes / LaunchWithDoc2 / LaunchWithDoc2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-13  |  15.7 KB  |  516 lines  |  [TEXT/MPS ]

  1. /*
  2.  *   LaunchWithDoc v 2.1
  3.  *
  4.  *   Greg Robbins  August 1993, 
  5.  *   modified by Nitin Ganatra, July 1994
  6.  *
  7.  *   Document-launching sample program
  8.  *   Loosely based on C.K. Haun's LaunchWithDoc
  9.  *
  10.  *   This snippet includes these useful routines:
  11.  *
  12.  *      OpenSpecifiedDocument
  13.  *        finds the creator application for a document, whether or not
  14.  *        the app is running, launches the app if necessary, and sends
  15.  *        the Apple Event necessary to get the app to open the document
  16.  *
  17.  *      FindApplicationFromDocument
  18.  *        searches the mounted volumes for the application which
  19.  *        created a document
  20.  *
  21.  *      LaunchApplicationWithDocument
  22.  *        launches an application which is not running and passes it
  23.  *        the OpenDocuments event as part of the launch parameters
  24.  *
  25.  *      SendOpenDocumentEventToProcess
  26.  *        sends an OpenDocuments Apple event to a running program
  27.  *
  28.  *      BuildOpenDocumentsEvent
  29.  *        utility function to build an 'odoc' event from a list of 
  30.  *        FSSpecs.
  31.  *
  32.  *  Remember that a target application need not be Apple event aware
  33.  *  in order for the OpenDocuments event to succeed (the System will
  34.  *  pull "puppet strings", simulating the events necessary to make the
  35.  *  target app open the document)
  36.  *
  37.  *  However, LaunchWithDoc must be high level event aware (as set in the
  38.  *  SIZE resource) in order to send Apple events using AESend
  39.  *
  40.  */
  41.  
  42. #include <QuickDraw.h>
  43. #include <StandardFile.h>
  44. #include <Fonts.h>
  45. #include <Menus.h>
  46. #include <Dialogs.h>
  47. #include <Events.h>
  48. #include <Files.h>
  49. #include <TextEdit.h>
  50. #include <Memory.h>
  51. #include <Errors.h>
  52. #include <Processes.h>
  53. #include <AppleEvents.h>
  54. #include <Aliases.h>
  55.  
  56. // prototypes
  57.  
  58. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr);
  59. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  60.     FSSpecPtr applicationFSSpecPtr);
  61. void ReportError(StringPtr messageStr);
  62.  
  63. // what functions changed since LaunchWithDoc2
  64.  
  65. OSErr BuildOpenDocumentEvent(ProcessSerialNumber *targetPSN, 
  66.             const FSSpec *theSpecArray, const short numOfSpecs, AppleEvent *odocAppleEvent);
  67. OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN,
  68.             const FSSpec *theSpecArray, const short numOfSpecs);
  69. OSErr LaunchApplicationWithDocument(const FSSpec *applicationFSSpecPtr,
  70.             const FSSpec *theSpecArray, const short numOfSpecs);
  71.  
  72.  
  73. // main program
  74. //
  75. // the main routine raises a std file dialog to let the
  76. // user choose a document and then opens the document
  77. // in the appropriate application
  78.  
  79. void main(void)
  80. {
  81.     OSErr retCode;
  82.     StandardFileReply documentStdFileReply;
  83.     SFTypeList mySFTypeList;
  84.     short index;
  85.     FSSpec    appSpec, docTmpSpec[3];
  86.     
  87.     // initialize the toolbox
  88.     InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus();
  89.     TEInit(); InitDialogs(nil); InitCursor();
  90.     
  91.     // Simplest case: get a document and open it
  92.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  93.     if (documentStdFileReply.sfGood) {
  94.         
  95.         retCode = OpenSpecifiedDocument(&documentStdFileReply.sfFile);
  96.         if (retCode != noErr) ReportError("\p OpenDocument failed");
  97.     }
  98.  
  99. /*    
  100.     // Another case: get an application to launch and three documents
  101.     // to open on startup
  102.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  103.     if (documentStdFileReply.sfGood) {
  104.  
  105.         appSpec = documentStdFileReply.sfFile;
  106.         for (index = 0; index < 3; index ++) {
  107.             StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  108.             if (documentStdFileReply.sfGood)
  109.                 docTmpSpec[index] = documentStdFileReply.sfFile;
  110.  
  111.         }
  112.         
  113.         retCode = LaunchApplicationWithDocument(&appSpec, docTmpSpec, index);
  114.     }
  115. */
  116.  
  117. }
  118.  
  119. void ReportError(StringPtr messageStr)
  120. {
  121.     DebugStr(messageStr);
  122. }
  123.  
  124.  
  125. // OpenSpecifiedDocument searches to see if the application which
  126. // created the document is already running.  If so, it sends
  127. // an OpenSpecifiedDocuments Apple event to the target application
  128. // (remember that, because of puppet strings, this works even
  129. // if the target application is not Apple event-aware.)
  130.  
  131. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr)
  132. {
  133.     OSErr retCode;
  134.     ProcessSerialNumber currPSN;
  135.     ProcessInfoRec currProcessInfo;
  136.     FSSpec applicationSpec;
  137.     FInfo documentFInfo;
  138.     Boolean foundRunningProcessFlag;
  139.     
  140.     // verify the document file exists and get its creator type
  141.     
  142.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  143.     if (retCode != noErr) goto Bail;
  144.     
  145.     // check the current processes to see if the creator app is already
  146.     // running, and get its process serial number (as currPSN)
  147.     
  148.     currPSN.lowLongOfPSN = kNoProcess;
  149.     currPSN.highLongOfPSN = 0;
  150.     
  151.     currProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
  152.     currProcessInfo.processName = nil;
  153.     currProcessInfo.processAppSpec = &applicationSpec;
  154.     
  155.     foundRunningProcessFlag = false;
  156.     while (GetNextProcess(&currPSN) == noErr) {
  157.         if (GetProcessInformation(&currPSN, &currProcessInfo) == noErr) {
  158.             if (currProcessInfo.processSignature == documentFInfo.fdCreator) {
  159.                 foundRunningProcessFlag = true;
  160.                 break;
  161.             }
  162.         }
  163.     }
  164.     
  165.     // if the creator is running, send it an OpenDocuments Apple event
  166.     // since there is no need to launch it
  167.     
  168.     if (foundRunningProcessFlag)
  169.         retCode = SendOpenDocumentEventToProcess(&currPSN, documentFSSpecPtr, 1);
  170.     
  171.     // else if the creator is not running, find it on disk and launch
  172.     // it with the OpenDocuments event included as a part of the
  173.     // launch parameters
  174.     
  175.     else {
  176.         retCode = FindApplicationFromDocument(documentFSSpecPtr, &applicationSpec);
  177.         
  178.         if (retCode == noErr)
  179.         
  180.             retCode = LaunchApplicationWithDocument(&applicationSpec,
  181.                 documentFSSpecPtr, 1);
  182.     }
  183.     
  184. Bail:
  185.     return retCode;
  186. }
  187.  
  188.  
  189. //----------------------------------------------------------------------------
  190. // LaunchApplicationWithDocument
  191. //
  192. // given an application and any number of documents, 
  193. // LaunchApplicationWithDocument launches the application and passes the 
  194. // application an OpenDocuments event for the document(s)
  195. //----------------------------------------------------------------------------
  196. OSErr LaunchApplicationWithDocument(
  197.     const FSSpec        *applicationFSSpecPtr,
  198.     const FSSpec         *theSpecArray,
  199.     const short            numOfSpecs)
  200. {
  201.     OSErr retCode;
  202.     LaunchParamBlockRec launchParams;
  203.     AppleEvent theAppleEvent;
  204.     AEDesc launchParamDesc;
  205.     ProcessSerialNumber targetPSN;
  206.     
  207.     // to simplify cleanup, ensure that handles are nil to start
  208.     launchParams.launchAppParameters    = nil;
  209.     theAppleEvent.dataHandle            = nil;
  210.     launchParamDesc.dataHandle            = nil;
  211.     
  212.     if (theSpecArray != nil) {
  213.  
  214.         // because 'odoc' events require a address descriptor, I just 
  215.         // grab the PSN for the current process.  It doesn't matter what
  216.         // it is, because it's never used.
  217.         (void) GetCurrentProcess(&targetPSN);
  218.         
  219.         // build an 'odoc' event given the array of FSSpecs and the ProcessSerialNumber
  220.         retCode = BuildOpenDocumentEvent(&targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
  221.         
  222.         if (retCode == noErr) {
  223.         
  224.             // coerce the AppleEvent into app parameters, for _LaunchApplication
  225.             retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc);
  226.             if (retCode != noErr) goto Bail;
  227.             
  228.             // fill in the launch parameter block, including the
  229.             // Apple event, and make the launch call
  230.             HLock((Handle) launchParamDesc.dataHandle);
  231.             launchParams.launchAppParameters =
  232.                 (AppParametersPtr) *(launchParamDesc.dataHandle);
  233.  
  234.         }
  235.  
  236.     }
  237.  
  238.     launchParams.launchBlockID        = extendedBlock;
  239.     launchParams.launchEPBLength    = extendedBlockLen;
  240.     launchParams.launchFileFlags    = launchNoFileFlags;
  241.     launchParams.launchControlFlags    = launchContinue;
  242.     launchParams.launchAppSpec        = (FSSpecPtr)applicationFSSpecPtr;
  243.  
  244.     retCode = LaunchApplication(&launchParams);
  245.  
  246. Bail:
  247.     // dispose of everything that was allocated
  248.     if (theAppleEvent.dataHandle != nil)     (void) AEDisposeDesc(&theAppleEvent);
  249.     if (launchParamDesc.dataHandle != nil)   (void) AEDisposeDesc(&launchParamDesc);
  250.     
  251.     return retCode;
  252.  
  253. }
  254.  
  255.  
  256. //----------------------------------------------------------------------------
  257. // SendOpenDocumentEventToProcess
  258. //
  259. // given an application's serial number and any number of documents, 
  260. // SendOpenDocumentEventToProcess passes 
  261. // the application an OpenDocuments event for the document
  262. //----------------------------------------------------------------------------
  263. OSErr SendOpenDocumentEventToProcess(
  264.     ProcessSerialNumber            *targetPSN,
  265.     const FSSpec                 *theSpecArray,
  266.     const short                    numOfSpecs)
  267. {
  268.     OSErr retCode;
  269.     AppleEvent theAppleEvent, theReplyEvent;
  270.  
  271.     theAppleEvent.dataHandle = nil;
  272.     retCode = BuildOpenDocumentEvent(targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
  273.  
  274.     if (retCode == noErr)
  275.         retCode = AESend(&theAppleEvent,
  276.                         &theReplyEvent, 
  277.                         kAENoReply, 
  278.                         kAENormalPriority,
  279.                         kAEDefaultTimeout,
  280.                         nil,
  281.                         nil);
  282.     
  283.     // dispose of the AppleEvent if it was allocated    
  284.     if (theAppleEvent.dataHandle != nil)  
  285.         (void) AEDisposeDesc(&theAppleEvent);
  286.     
  287.     return retCode;
  288.  
  289. }
  290.  
  291.  
  292. // FindApplicationFromDocument uses the Desktop Database to
  293. // locate the creator application for the given document
  294. //
  295. // this routine will first check the desktop database of the disk
  296. // containing the document, then the desktop database of all local
  297. // disks, then the desktop databases of all server volumes
  298. // (so up to three passes will be made)
  299.  
  300. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  301.     FSSpecPtr applicationFSSpecPtr)
  302. {
  303.     enum { documentPass, localPass, remotePass, donePass } volumePass;
  304.     DTPBRec desktopParams;
  305.     HParamBlockRec hfsParams;
  306.     FInfo documentFInfo;
  307.     short volumeIndex;
  308.     Boolean foundFlag;
  309.     GetVolParmsInfoBuffer volumeInfoBuffer;
  310.     OSErr retCode;
  311.     
  312. // dkj 12/94 initialize flag to false (thanks to Peter Baral for pointing out this bug)
  313.     foundFlag = false;
  314.  
  315.     // verify the document file exists and get its creator type
  316.     
  317.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  318.     if (retCode != noErr) goto Bail;
  319.     
  320.     volumePass = documentPass;
  321.     volumeIndex = 0;
  322.     
  323.     do {
  324.         
  325.         // first, find the vRefNum of the volume whose Desktop Database
  326.         // we're checking this time
  327.         
  328.         // if we're on the initial pass (documentPass) just use
  329.         // the vRefNum of the document itself
  330.         
  331.         if (volumePass == documentPass)
  332.         
  333.             desktopParams.ioVRefNum = documentFSSpecPtr->vRefNum;
  334.         
  335.         // otherwise, find the vRefNum of the next volume appropriate
  336.         // for this pass
  337.         
  338.         else {
  339.             
  340.             volumeIndex++;
  341.             
  342.             // convert the volumeIndex into a vRefNum
  343.             
  344.             hfsParams.volumeParam.ioNamePtr = nil;
  345.             hfsParams.volumeParam.ioVRefNum = 0;
  346.             hfsParams.volumeParam.ioVolIndex = volumeIndex;
  347.             retCode = PBHGetVInfoSync(&hfsParams);
  348.             
  349.             // a nsvErr indicates that the current pass is over
  350.             if (retCode == nsvErr) goto SkipThisVolume;
  351.             if (retCode != noErr) goto Bail;
  352.             
  353.             // since we handled the document volume during the documentPass,
  354.             // skip it if we have hit that volume again
  355.             
  356.             if (hfsParams.volumeParam.ioVRefNum == documentFSSpecPtr->vRefNum)
  357.                 goto SkipThisVolume;
  358.             
  359.             // call GetVolParms to determine if this volume is a server
  360.             // (a remote volume)
  361.             
  362.             hfsParams.ioParam.ioBuffer = (Ptr) &volumeInfoBuffer;
  363.             hfsParams.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
  364.             retCode = PBHGetVolParmsSync(&hfsParams);
  365.             if (retCode != noErr) goto Bail;
  366.             
  367.             // if the vMServerAdr field of the volume information buffer
  368.             // is zero, this is a local volume; skip this volume
  369.             // if it's local on a remote pass or remote on a local pass
  370.             
  371.             if ((volumeInfoBuffer.vMServerAdr != 0) !=
  372.                 (volumePass == remotePass)) goto SkipThisVolume;
  373.             
  374.             // okay, now we've found the vRefNum for our desktop database call
  375.             
  376.             desktopParams.ioVRefNum = hfsParams.volumeParam.ioVRefNum;
  377.         }
  378.         
  379.         // find the path refNum for the desktop database for
  380.         // the volume we're interested in
  381.         
  382.         desktopParams.ioNamePtr = nil;
  383.         
  384.         retCode = PBDTGetPath(&desktopParams);
  385.         if (retCode == noErr && desktopParams.ioDTRefNum != 0) {
  386.         
  387.             // use the GetAPPL call to find the preferred application
  388.             // for opening any document with this one's creator
  389.             
  390.             desktopParams.ioIndex = 0;
  391.             desktopParams.ioFileCreator = documentFInfo.fdCreator;
  392.             desktopParams.ioNamePtr = applicationFSSpecPtr->name;
  393.             retCode = PBDTGetAPPLSync(&desktopParams);
  394.             
  395.             if (retCode == noErr) {
  396.             
  397.                 // okay, found it; fill in the application file spec
  398.                 // and set the flag indicating we're done
  399.                 
  400.                 applicationFSSpecPtr->parID = desktopParams.ioAPPLParID;
  401.                 applicationFSSpecPtr->vRefNum = desktopParams.ioVRefNum;
  402.                 foundFlag = true;
  403.                 
  404.             }
  405.         }
  406.         
  407.     SkipThisVolume:
  408.     
  409.         // if retCode indicates a no such volume error or if this
  410.         // was the first pass, it's time to move on to the next pass
  411.         
  412.         if (retCode == nsvErr || volumePass == documentPass) {
  413.             volumePass++;
  414.             volumeIndex = 0;
  415.         }
  416.         
  417.     } while (foundFlag == false && volumePass != donePass);
  418.     
  419. Bail:
  420.     return retCode;
  421. }
  422.  
  423.  
  424. //----------------------------------------------------------------------------
  425. // BuildOpenDocumentsEvent
  426. //
  427. // General utility to turn a ProcessSerialNumber and a list of FSSpecs into
  428. // an 'odoc' AppleEvent with the ProcessSerialNumber as the target
  429. // application.  Used by SendOpenDocumentEventToProcess, and
  430. // LaunchApplicationWithDocument.
  431. //----------------------------------------------------------------------------
  432. OSErr BuildOpenDocumentEvent(
  433.     ProcessSerialNumber        *targetPSN, 
  434.     const FSSpec             *theSpecArray, 
  435.     const short                numOfSpecs,
  436.     AppleEvent                *odocAppleEvent)
  437. {
  438.     OSErr            retCode;
  439.     AppleEvent        theAppleEvent;
  440.     AEDesc            targetAddrDesc, docDesc;
  441.     AEDescList        docDescList;
  442.     AliasHandle        docAlias;
  443.     short            counter;
  444.     FSSpecPtr        specIterator;
  445.  
  446.     // to simplify cleanup, ensure that handles are nil to start
  447.     targetAddrDesc.dataHandle    = nil;
  448.     theAppleEvent.dataHandle    = nil;
  449.     docDescList.dataHandle        = nil;
  450.     docDesc.dataHandle            = nil;
  451.     docAlias                    = nil;
  452.  
  453.     // create an address descriptor based on the serial number of
  454.     // the target process
  455.     retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN,
  456.         sizeof(ProcessSerialNumber), &targetAddrDesc);
  457.     if (retCode != noErr) goto Bail;
  458.     
  459.     // make a descriptor list containing just a descriptor with an
  460.     // alias to the document
  461.     retCode = AECreateList(nil, 0, false, &docDescList);
  462.     if (retCode != noErr) goto Bail;
  463.  
  464.     // start at the beginning of the FSSpec list, and start adding
  465.     // them to the document list descriptor
  466.  
  467.     // NOTE: we need to make sure we dispose of the aliases and the
  468.     // AE descriptor in this loop, otherwise there will be memory
  469.     // leaks.
  470.     specIterator = (FSSpecPtr)theSpecArray;
  471.     for (counter = 0; counter < numOfSpecs; counter ++) {    
  472.  
  473.         retCode = NewAlias(nil, &specIterator[counter], &docAlias);
  474.         if (retCode != noErr) goto Bail;
  475.         
  476.         HLock((Handle) docAlias);
  477.         retCode = AECreateDesc(typeAlias, (Ptr) *docAlias, 
  478.             InlineGetHandleSize((Handle) docAlias), &docDesc);
  479.         HUnlock((Handle) docAlias);
  480.         
  481.         if (retCode != noErr) goto Bail;
  482.         // the alias is now in the AEDescriptor, so dispose of it
  483.         DisposeHandle((Handle)docAlias); docAlias = nil;
  484.         
  485.         retCode = AEPutDesc(&docDescList, 0, &docDesc);
  486.         if (retCode != noErr) goto Bail;
  487.  
  488.         // the alias is now in the AE document list, so dispose of it
  489.         retCode = AEDisposeDesc(&docDesc);
  490.         if (retCode != noErr) goto Bail;
  491.  
  492.     }
  493.     
  494.     // now make the 'odoc' AppleEvent descriptor and insert the 
  495.     // document descriptor list as the direct object
  496.     retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  497.         &targetAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
  498.         &theAppleEvent);
  499.     if (retCode != noErr) goto Bail;
  500.     
  501.     retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
  502.     if (retCode != noErr) goto Bail;
  503.     
  504.     *odocAppleEvent = theAppleEvent;
  505.  
  506. Bail:
  507.     // dispose of everything that was allocated, except the return AE
  508.     if (targetAddrDesc.dataHandle != nil)  (void) AEDisposeDesc(&targetAddrDesc);
  509.     if (docDescList.dataHandle != nil)  (void) AEDisposeDesc(&docDescList);
  510.     if (docDesc.dataHandle != nil)  (void) AEDisposeDesc(&docDesc);
  511.     if (docAlias != nil)  DisposeHandle((Handle) docAlias);
  512.     
  513.     return retCode;
  514.  
  515. }
  516.